home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1997 September / Macworld (1997-09).dmg / Serious Software / Cherwell Scientific Demos / pro Fit / pro Fit 5.0 demo (fpu).sea / pro Fit 5.0 demo (fpu) / External Modules / External modules sources / C / Contour plotting / ContourFunction.c < prev    next >
Text File  |  1996-03-19  |  17KB  |  555 lines

  1.  
  2.  
  3. #include "proFit_interface.h"
  4.  
  5. #ifndef __MODULE_UTILITIES__
  6. #include "ModuleUtilities.h"
  7. #endif
  8. #ifndef __STRING_SUPPORT__
  9. #include "StringSupport.h"
  10. #endif
  11. #ifndef __CONTOUR_PLOTTER__
  12. #include "ContourPlotter.h"
  13. #endif
  14.  
  15. /* the following struct contains the variables that we use in Run() */
  16. /* We collect them into a struct such that we can pass them all simply */
  17. /* by passing a pointer to this struct */
  18.  
  19.  
  20. typedef struct
  21. {
  22.     double        firstLevel;                /* default values */
  23.     double        lastLevel;
  24.     double        levelStep;
  25.     short        x1Param, x2Param;        // the parameters for x1, x2 (0 if =x)
  26.     long        x1Res, x2Res;            // resolution along x1, x2
  27.     double        x1Min, x1Max;            // range for x1
  28.     double        x2Min, x2Max;            // range for x2
  29.     Boolean        useCurrentGraph;        // true if we should draw into current graph
  30.     long        customContourWindow;    // 0 or ref to a custom contour level window 
  31.     long        customContourColumn;    // 0 or id of a custom contour column
  32. }MyStaticVars;
  33.  
  34.  
  35.  
  36.  
  37. /***************************************************************************************/
  38.  
  39. void SetUp (    short* const moduleKind,        /* return isFunction or isProgram */
  40.                 Str255 name,                    /* the name of the program of function */
  41.                                                 /*  (pascal string) */
  42.                 long* const requiredGlobals,    /* the number of bytes to be allocated */
  43.                                                 /*  in ExtModulesParamBlock.globals. */
  44.                                                 /* Set to 0 if you don't use this feature */
  45.                 ExtModulesParamBlock* pb)        /*     can be ignored in most cases */
  46.     /* called when your code resource is linked to proFit */
  47. {
  48.     *moduleKind=isProgram;                                /* we define a program */
  49.     SetPascalStr(name,"\pContour Plot Function...",255);    /* its name */
  50.     *requiredGlobals = sizeof(MyStaticVars);    /* our static vars */
  51. }
  52.  
  53.  
  54.  
  55. /***************************************************************************************/
  56.  
  57. void InitializeProg (ExtModulesParamBlock* pb)
  58.     /* Can be left emtpy if not needed. */
  59.     /* Only used by advanced programmers. */
  60.     /* called when the resource is linked to proFit after SetUp was called */
  61. {
  62.     MyStaticVars* vs = (MyStaticVars*)pb->globals;
  63.     vs->firstLevel = -1;                            // set default values
  64.     vs->lastLevel = 1;
  65.     vs->levelStep = 0.2;
  66.     vs->x1Param = 0;
  67.     vs->x2Param = 1;
  68.     vs->x1Res = 100;
  69.     vs->x2Res = 100;
  70.     vs->x1Min = -1;
  71.     vs->x1Max = 1;
  72.     vs->x2Min = -1;
  73.     vs->x2Max = 1;
  74.     vs->useCurrentGraph = true;
  75.     vs->customContourWindow = 0;
  76.     vs->customContourColumn = 0;
  77. }
  78.  
  79. /***************************************************************************************/
  80.  
  81. enum {
  82.     firstLevelTitleItem = 3,
  83.     lastLevelTitleItem,
  84.     distanceTitleItem,
  85.     firstLevelItem = 6,
  86.     lastLevelItem,
  87.     distanceItem,
  88.     x1PopupItem = 9,
  89.     x1FromItem = 11,
  90.     x1ToItem = 13,
  91.     x1ResItem = 20,
  92.     x2PopupItem = 14,
  93.     x2FromItem = 16,
  94.     x2ToItem = 18,
  95.     x2ResItem = 22,
  96.     useCurrentGraphItem = 23,
  97.     regularContourItem = 24,
  98.     customContourItem = 25,
  99.     windowItem = 26,
  100.     columnItem = 27,
  101.     helpButtonItem = 28
  102.     };
  103.  
  104. static void CreateParamPopup(DialogData* dlg, short itemID, short value)
  105.     // creates a popup for X1, X2
  106. {
  107.     MenuHandle        menu;
  108.     short            i, nrParams;
  109.     Str255            name;
  110.     ControlHandle    control = (ControlHandle)MyGetItemHandle(dlg,itemID);
  111.     if (control==nil) return;                            // should never happen
  112.     menu = (**(PopupPrivateData**)(**control).contrlData).mHandle;
  113.     if (menu==nil) return;                                // should never happen
  114.     nrParams = GetNumFunctionParams("\p");                // number of parameters of current function
  115.     for (i=1; i<=nrParams; i++)
  116.     {
  117.         GetFunctionParamName("\p",i,name);
  118.         AddItemToMenu(menu, name);
  119.     }
  120.     SetControlMaximum(control, nrParams+2);
  121.     if (value > nrParams) value = nrParams;
  122.     SetControlValue(control, value? value+2: value+1);
  123. }
  124.  
  125. static short GetParamPopup(DialogData* dlg, short itemID)
  126. {
  127.     short    value = MyGetItemValue(dlg, itemID);
  128.     if (value > 1) return value-2;
  129.     else return value-1;
  130. }
  131.  
  132. static Boolean GetResolution(DialogData* dlg, short itemID, long* const value)
  133. {
  134.     long val;
  135.     if (MyGetLongItem(dlg, itemID, &val) == false) return false;
  136.     if (val < 2 || val > 10000)
  137.     {    MySelectItemText(dlg, itemID, 0, 0x7FFF);
  138.         SysBeep(30);
  139.         return false;
  140.     }
  141.     *value = val;
  142.     return true;
  143. }
  144.  
  145. static double CallMyFunction(short x1Param, short x2Param, double x1Value, double x2Value, double x)
  146.     // param=0: x, otherwise param > 0
  147. {
  148.     if (x1Param != 0) SetFunctionParam("\p",x1Param, x1Value);
  149.     else x = x1Value;
  150.     if (x2Param != 0) SetFunctionParam("\p",x2Param, x2Value);
  151.     else x = x2Value;
  152.     return CallFunction("\p", x);
  153. }
  154.  
  155. static Boolean MyFunction(double x1, double x2, double* const x3, void* param)
  156. {
  157.     MyStaticVars* vs = (MyStaticVars*)param;
  158.     short    x1Param = vs->x1Param;
  159.     short    x2Param = vs->x2Param;
  160.     double    x = 0;
  161.     Str255    dummyString;
  162.  
  163.     if (x1Param != 0) SetFunctionParam("\p",x1Param, x1);
  164.     else x = x1;
  165.     if (x2Param != 0) SetFunctionParam("\p",x2Param, x2);
  166.     else x = x2;
  167.     *x3 = CallFunction("\p", x);
  168.     if (GetAndSetStatus(-1, dummyString) == 3)            // if a run-time error occured
  169.     {    GetAndSetStatus(0, dummyString);                // then reset it
  170.         return false;
  171.     }
  172.     return !NumberInvalid(*x3);
  173. }//MyFunction
  174.  
  175. static void ShowHideItems(DialogData* const dlg)
  176.     // shows and hides the items in the lower part of the dialog
  177. {
  178.     static const char regularItems[] = {firstLevelTitleItem, lastLevelTitleItem,
  179.             distanceTitleItem, firstLevelItem, lastLevelItem, distanceItem, 0};
  180.     static const char customItems[] = {windowItem, columnItem, 0};
  181.  
  182.     char*    itemsToShow;
  183.     char*    itemsToHide;
  184.  
  185.     if (MyGetItemValue(dlg, regularContourItem))
  186.     {    itemsToShow = (char*)regularItems;
  187.         itemsToHide = (char*)customItems;
  188.     }
  189.     else
  190.     {    itemsToShow = (char*)customItems;
  191.         itemsToHide = (char*)regularItems;
  192.     }
  193.     while (*itemsToShow) MyShowItem(dlg, *itemsToShow++);
  194.     while (*itemsToHide) MyHideItem(dlg, *itemsToHide++);
  195. }
  196.  
  197.  
  198. void Run(ExtModulesParamBlock* pb)
  199.     /* called to execute the program */
  200. {
  201.     PlottingData* v=nil;
  202.     MyStaticVars* vs = (MyStaticVars*)pb->globals;
  203.     Boolean        useCurrentGraph = true;
  204.     Handle        myMemory = nil;
  205.     long        oldDataWindow = GetCurrentWindow(dataType);
  206.  
  207.     if (TestStop()) return;
  208.  
  209.     {    DialogData*    dlg;
  210.         short        itemHit;
  211.         short        resFile = FSpOpenResFile(GetModuleFile(), fsCurPerm);
  212.         double        x1Min = vs->x1Min, x1Max = vs->x1Max;
  213.         double        x2Min = vs->x2Min, x2Max = vs->x2Max;
  214.         long        customContourWindow = vs->customContourWindow;
  215.         long        customContourColumn = vs->customContourWindow;
  216.         Boolean        customContours;
  217.         
  218.         if (resFile == -1) return;
  219.  
  220.         dlg = MyGetNewDialog(30000, 1, 2);
  221.         if (dlg == nil) StopExecution();
  222.         else
  223.         {
  224.             if (FrontmostWindow(drawingType) == 0 || GetCurrentGraph() == 0)    // if no drawing window or now current graph
  225.             {    useCurrentGraph = false;
  226.                 MyHiliteItem(dlg, useCurrentGraphItem, 255);        // disable
  227.             } else                                                    // if there's a current graph
  228.                 GetGraphCoordinates (&x1Min, &x1Max, &x2Min, &x2Max);    // use range of graph as default
  229.  
  230.             if (vs->customContourWindow != 0 && GetWindowType(vs->customContourWindow)==0)    // if window does not exist anymore
  231.             {    vs->customContourWindow = 0;
  232.                 vs->customContourColumn = 0;
  233.             }
  234.             else if (vs->customContourWindow)
  235.             {
  236.                 SetCurrentWindow(vs->customContourWindow);
  237.                 if (vs->customContourColumn > NrCols() || GetColType(vs->customContourColumn) == textColumn)            // if window has become smaller
  238.                     vs->customContourColumn = 1;
  239.                 SetCurrentWindow(oldDataWindow);
  240.             }
  241.             customContours = vs->customContourWindow != 0;
  242.  
  243.             MySetExtendedItem(dlg, firstLevelItem, vs->firstLevel);
  244.             MySetExtendedItem(dlg, lastLevelItem, vs->lastLevel);
  245.             MySetExtendedItem(dlg, distanceItem, vs->levelStep);
  246.             MySetExtendedItem(dlg, x1FromItem, x1Min);
  247.             MySetExtendedItem(dlg, x1ToItem, x1Max);
  248.             MySetExtendedItem(dlg, x2FromItem, x2Min);
  249.             MySetExtendedItem(dlg, x2ToItem, x2Max);
  250.             MySetLongItem(dlg, x1ResItem, vs->x1Res);
  251.             MySetLongItem(dlg, x2ResItem, vs->x2Res);
  252.             CreateParamPopup(dlg, x1PopupItem, vs->x1Param);
  253.             CreateParamPopup(dlg, x2PopupItem, vs->x2Param);
  254.             MySetItemValue(dlg, useCurrentGraphItem, useCurrentGraph);
  255.  
  256.             MakeWindowPopup(dlg, windowItem, dataType);
  257.             if (vs->customContourWindow)
  258.                 SetItemGeneral(dlg, windowItem, &vs->customContourWindow);
  259.             MakeColumnPopup(dlg, columnItem, windowItem, 1);
  260.             if (vs->customContourColumn)
  261.                 MySetItemValue(dlg, columnItem, vs->customContourColumn);
  262.             MySetItemValue(dlg, regularContourItem, !customContours);
  263.             MySetItemValue(dlg, customContourItem, customContours);
  264.             if (FrontmostWindow(dataType)==0)                        // if no data window
  265.                 MyHiliteItem(dlg, customContourItem, 255);            // disable custom
  266.  
  267.             ShowHideItems(dlg);
  268.             MySelectItemText(dlg, x1FromItem, 0, 0x7FFF);
  269.             MyShowDialog(dlg);
  270.             do
  271.             {
  272.                 MyModalDialog(dlg, &itemHit);
  273.  
  274.                 switch(itemHit)
  275.                 {
  276.  
  277.                 case regularContourItem:
  278.                         MySetItemValue(dlg, customContourItem, 0);
  279.                         ShowHideItems(dlg);
  280.                         break;
  281.                 case customContourItem:
  282.                         MySetItemValue(dlg, regularContourItem, 0);
  283.                         ShowHideItems(dlg);
  284.                         break;
  285.                 case helpButtonItem:
  286.                         MyAlert(30003);                    // bring up help alert
  287.                         break;
  288.                 case ok:
  289.                     {
  290.                         double    firstLevel;
  291.                         double    lastLevel;
  292.                         double    levelStep;
  293.                         long    x1Res, x2Res;
  294.                         short    x1Param = GetParamPopup(dlg, x1PopupItem);
  295.                         short    x2Param = GetParamPopup(dlg, x2PopupItem);
  296.                         
  297.                         if (x1Param == x2Param)
  298.                         {    SysBeep(30);
  299.                             MyAlert(30002);                                        // you must select different x and y parameters
  300.                             itemHit = 0;
  301.                         } else if (!(    MyGetExtendedItem(dlg, firstLevelItem, &firstLevel) &&
  302.                                 MyGetExtendedItem(dlg, lastLevelItem, &lastLevel) &&
  303.                                 MyGetExtendedItem(dlg, distanceItem, &levelStep) &&
  304.                                 MyGetExtendedItem(dlg, x1FromItem, &x1Min) &&
  305.                                 MyGetExtendedItem(dlg, x1ToItem, &x1Max) &&
  306.                                 MyGetExtendedItem(dlg, x2FromItem, &x2Min) &&
  307.                                 MyGetExtendedItem(dlg, x2ToItem, &x2Max) &&
  308.                                 GetResolution(dlg, x1ResItem, &x1Res) &&
  309.                                 GetResolution(dlg, x2ResItem, &x2Res)))
  310.                                     itemHit = 0;                                // if bad entry
  311.                         else if (firstLevel > lastLevel)
  312.                         {    MySelectItemText(dlg, firstLevelItem, 0, 0x7FFF);
  313.                             SysBeep(30);
  314.                             itemHit = 0;
  315.                         }
  316.                         else if (levelStep <= 1e-100 || (lastLevel-firstLevel)/levelStep > 1000)
  317.                         {    MySelectItemText(dlg, distanceItem, 0, 0x7FFF);
  318.                             SysBeep(30);
  319.                             itemHit = 0;
  320.                         }
  321.                         else if (x1Min >= x1Max)
  322.                         {    MySelectItemText(dlg, x1FromItem, 0, 0x7FFF);
  323.                             SysBeep(30);
  324.                             itemHit = 0;
  325.                         }
  326.                         else if (x2Min >= x2Max)
  327.                         {    MySelectItemText(dlg, x2FromItem, 0, 0x7FFF);
  328.                             SysBeep(30);
  329.                             itemHit = 0;
  330.                         }
  331.                         else
  332.                         {
  333.                             if (MyGetItemValue(dlg, regularContourItem))    // if regular contour levels
  334.                             {    vs->firstLevel = firstLevel;
  335.                                 vs->lastLevel = lastLevel;
  336.                                 vs->levelStep = levelStep;
  337.                                 vs->customContourWindow = 0;
  338.                                 vs->customContourColumn = 0;
  339.                             } else
  340.                             {    GetItemGeneral(dlg, windowItem, &vs->customContourWindow);    // get window
  341.                                 vs->customContourColumn = MyGetItemValue(dlg, columnItem);    // and column
  342.                             }
  343.                             vs->x1Res = x1Res;
  344.                             vs->x2Res = x2Res;
  345.                             vs->x1Min = x1Min;
  346.                             vs->x1Max = x1Max;
  347.                             vs->x2Min = x2Min;
  348.                             vs->x2Max = x2Max;
  349.                             vs->x1Param = GetParamPopup(dlg, x1PopupItem);
  350.                             vs->x2Param = GetParamPopup(dlg, x2PopupItem);
  351.                             useCurrentGraph = MyGetItemValue(dlg, useCurrentGraphItem);
  352.                         }
  353.                     }// case ok
  354.                 }//switch
  355.             }while (itemHit != ok && itemHit != cancel);
  356.             if (itemHit == cancel) StopExecution();
  357.             MyDisposeDialog(dlg);
  358.         }
  359.  
  360.  
  361.         CloseResFile(resFile);
  362.     }
  363.     if (TestStop()) return;
  364.  
  365.     SetWaitTitle("\pCalculating matrix");
  366.     {
  367.         double    x1Step = (vs->x1Max - vs->x1Min)/vs->x1Res;
  368.         double    x2Step = (vs->x2Max - vs->x2Min)/vs->x2Res;
  369.         double    origX1, origX2;
  370.  
  371.         if (vs->x1Param) origX1 = GetFunctionParam("\p", vs->x1Param);    // save current params for restoring them later
  372.         if (vs->x2Param) origX2 = GetFunctionParam("\p", vs->x2Param);    // save current params for restoring them later
  373.  
  374.         v = InitContourPlotter(vs->x1Res+1, vs->x2Res+1, vs->x1Min, vs->x1Max,
  375.                 vs->x2Min, vs->x2Max);
  376.         if (v==nil)
  377.         {    StopExecution();
  378.             goto done;
  379.         }
  380.  
  381.         if (!CalculateContourMatrix(MyFunction, vs, v))
  382.         {    StopExecution();
  383.             goto done;
  384.         }
  385.  
  386.         if (vs->x1Param) SetFunctionParam("\p", vs->x1Param, origX1);    // restore params
  387.         if (vs->x2Param) SetFunctionParam("\p", vs->x2Param, origX2);    // restore params
  388.  
  389.     }
  390.  
  391.     SetLineStyle(1,1);
  392.     SetLineColor(0,0,0);
  393.     if (useCurrentGraph == false)
  394.     {    CreateNewGraph(vs->x1Min, vs->x1Max, vs->x2Min, vs->x2Max, 0, 0);
  395.         if (TestStop()) goto done;
  396.     }
  397.  
  398.  
  399.     DisableDrawingUpdates();                        // avoids flicker in drawing window
  400.  
  401.     {
  402.         Boolean        allLevelsDone=false;
  403.         double        level;
  404.         long        row = 1;
  405.         Boolean        regularLevels = vs->customContourWindow==0;        // true if regular levels, false if custom levels
  406.         long        levelCol = vs->customContourColumn;                // the column for custom levels
  407.         long        thickCol=0, dashCol=0, rCol=0, gCol=0, bCol=0;    // the columns for thickness, dash, rgb-values
  408.         long        nameCol=0;
  409.  
  410.  
  411.         if (regularLevels)
  412.             level = vs->firstLevel;
  413.         else                                            // parse for special additional columns
  414.         {
  415.             Boolean columnsDone = false;
  416.             long i = levelCol+1;
  417.             SetCurrentWindow(vs->customContourWindow);
  418.             while (i<=NrCols() && !columnsDone)
  419.             {
  420.                 Str255    s;
  421.                 GetColName(s, i);
  422.                 if (GetColType(i) != textColumn)
  423.                 {
  424.                     if (EqualStr255(s, "\pthickness"))
  425.                         thickCol = i;
  426.                     else if (EqualStr255(s, "\pdash"))
  427.                         dashCol = i;
  428.                     else if (EqualStr255(s, "\pr"))
  429.                         rCol = i;
  430.                     else if (EqualStr255(s, "\pg"))
  431.                         gCol = i;
  432.                     else if (EqualStr255(s, "\pb"))
  433.                         bCol = i;
  434.                     else columnsDone = true;
  435.                 }
  436.                 else if (EqualStr255(s, "\pname"))
  437.                     nameCol = i;
  438.                 else columnsDone = true;
  439.                 i += 1;
  440.             }
  441.             if (rCol == 0 || gCol == 0 || bCol == 0)        // they must all be != 0
  442.                 rCol = gCol = bCol = 0;
  443.             if (oldDataWindow) SetCurrentWindow(oldDataWindow);
  444.         }
  445.  
  446.         while (!allLevelsDone)
  447.         {
  448.             Str255    curveName;
  449.             Str255    status;
  450.             double    thick = 1.0;
  451.             double    dash = 1;
  452.             double    r=0, g=0, b=0;
  453.  
  454.             if (!regularLevels)
  455.             {
  456.                 SetCurrentWindow(vs->customContourWindow);
  457.                 while (!TestData(row, levelCol))
  458.                 {    if (row >= NrRows())
  459.                     {    allLevelsDone = true;
  460.                         break;                                // if all done
  461.                     }
  462.                     row += 1;
  463.                 }
  464.                 if (allLevelsDone) break;
  465.                 level = GetData(row, levelCol);
  466.                 if (thickCol != 0 && TestData(row, thickCol))
  467.                     thick = GetData(row, thickCol);
  468.                 if (dashCol != 0 && TestData(row, dashCol))
  469.                     dash = GetData(row, dashCol);
  470.                 if (rCol != 0 && TestData(row, rCol) && TestData(row, gCol) && TestData(row, bCol))
  471.                 {    r = GetData(row, rCol);
  472.                     g = GetData(row, gCol);
  473.                     b = GetData(row, bCol);
  474.                 }
  475.                 if (nameCol != 0)
  476.                     GetCell(curveName, row, nameCol);
  477.                 row += 1;
  478.                 if (row > NrRows()) allLevelsDone = true;
  479.  
  480.                 if (oldDataWindow) SetCurrentWindow(oldDataWindow);
  481.             }
  482.  
  483.             if (nameCol == 0)
  484.                 NumberToStr255(level, curveName, 0, 3);
  485.             Concat2(status, "\plevel: ", curveName);
  486.  
  487.             SetLineStyle(thick, dash);
  488.             SetLineColor(r,g,b);
  489.  
  490.             SetWaitTitle(status);
  491.             OpenCurve(curveName);
  492.             if (!DrawContour(level, v)) goto done;
  493.             CloseCurve();
  494.             SetLineStyle(1,1);
  495.             SetLineColor(0,0,0);
  496.             if (regularLevels)
  497.             {    level += vs->levelStep;
  498.                 allLevelsDone = level > vs->lastLevel;
  499.             }
  500.  
  501.         }
  502.     }
  503.  
  504.  
  505.  
  506. done:                                                // clean up
  507.     DisposeContourPlotter(v);                        // does nothing if v==nil
  508.     if (oldDataWindow) SetCurrentWindow(oldDataWindow);
  509.     if (myMemory != nil) DisposeHandle(myMemory);
  510. }
  511.  
  512. /***************************************************************************************/
  513.  
  514. void CleanUp (ExtModulesParamBlock* pb)
  515.     /* called when your code resource is not needed anymore */
  516.     /* in most cases, this function can be empty */
  517. {
  518. }
  519.  
  520.  
  521. /***************************************************************************************/
  522.                         /* for functions, not used here: */
  523. /***************************************************************************************/
  524.  
  525. void InitializeFunc (
  526.                 Boolean* const hasDerivatives,    /* set to true if you define the function Derivatives to calculate the partial derivatives of your parameters */
  527.                 Str255 descr1stLine,            /* string 1 to appear in parameter window */
  528.                 Str255 descr2ndLine,            /* string 2 to appear in parameter window */
  529.                 short* const numberOfParams,    /* the number of parameters of your function */
  530.                 DefaultParamInfo* const a0,            /* the default names, values etc. of your parameters */
  531.                 ExtModulesParamBlock* pb)        /* can be ignored in most cases */
  532. {}
  533.  
  534. void Func (        double x,                        /* the x-value */
  535.                 ParamArray a,                    /* the parameters */
  536.                 double* const y,                /* the y-value to be returned */
  537.                 ExtModulesParamBlock* pb)        /* can be ignored in most cases */
  538. {}
  539.  
  540. void Derivatives(double x,                        /* the x-value */
  541.                 ParamArray a,                    /* the parameters */
  542.                 ParamArray dyda,                /* the derivatives to be returned */
  543.                 ExtModulesParamBlock* pb)
  544. {}
  545. short Check(short paramNo,                        /* the parameter that was changed */
  546.                 DefaultParamInfo* const a0,            /* the default names, values etc of the paramters */
  547.                 ExtModulesParamBlock* pb)
  548. {paramNo--;/*the array a0 is indexed from zero. Decrement paramNo so that it corresponds to the inices of a0*/
  549.     return ok;
  550. }
  551. void First (    ParamArray a,                    /* the new parameters */
  552.                 ExtModulesParamBlock* pb)
  553. {}
  554. void Last (ExtModulesParamBlock* pb)
  555. {}